package com.pai4j.pgc.service.community;
import com.pai4j.common.enums.RedisKeyEnum;
import com.pai4j.common.enums.community.ResourceTypeEnum;
import com.pai4j.common.util.JsonUtil;
import com.pai4j.common.util.RedisUtil;
import com.pai4j.domain.community.vo.response.cc.CTCExperienceBaseVO;
import com.pai4j.domain.community.vo.response.cc.CTCQuestionBaseVO;
import com.pai4j.domain.community.vo.response.cc.CTCVideoBaseVO;
import com.pai4j.domain.dto.resource.ResourceBaseDTO;
import com.pai4j.domain.dto.resource.ResourceDTO;
import com.pai4j.pgc.dao.community.IUserStudyHistoryDAO;
import com.pai4j.pgc.entity.community.UserStudyHistoryEntity;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * 社区内容学习历史纪录
 *
 */
@Service
public class StudyHistoryService {


    @Autowired
    @Qualifier(value = "pointExecutor")
    private Executor pointExecutor;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private IUserStudyHistoryDAO userStudyHistoryDAO;

    @Autowired
    private CCExperienceService experienceService;
    @Autowired
    private CCVideoService videoService;
    @Autowired
    private CCQuestionService questionService;

    /**
     * 加入学习历史纪录
     *
     * @param userid
     * @param resourceType
     * @param resourceId
     * @return
     */
    public void add(String userid, ResourceTypeEnum resourceType, Long resourceId) {
        pointExecutor.execute(() -> {
            doAddHistory(userid, resourceType, resourceId);
        });
    }

    private void doAddHistory(String userid, ResourceTypeEnum resourceType, Long resourceId) {

        // 写redis
        this.add2RedisCache(userid, resourceType, resourceId);
        // 写数据库
        this.saveOrUpdateDB(userid, resourceType, resourceId);
    }

    /**
     * 保存或更新用户学习纪录
     *
     *
     * @param userid
     * @param resourceType
     * @param resourceId
     */
    private void saveOrUpdateDB(String userid, ResourceTypeEnum resourceType, Long resourceId) {
        UserStudyHistoryEntity entity = userStudyHistoryDAO.findAllByUserIdAndResourceTypeAndResourceId(
                userid, resourceType.getType(), resourceId);
        if (entity == null) {
            entity = new UserStudyHistoryEntity();
            entity.setUserId(userid);
            entity.setResourceType(resourceType.getType());
            entity.setResourceId(resourceId);
        }
        entity.setLastDate(new Date());
        userStudyHistoryDAO.save(entity);
    }

    private void add2RedisCache(String userid, ResourceTypeEnum resourceType, Long resourceId) {

        String cacheKey = historyCacheKey(userid);
        if (redisUtil.exists(cacheKey)) {
            // 缓存未失效，直接加入缓存
            ResourceBaseDTO cacheObj = new ResourceBaseDTO(resourceType.getType(), resourceId);
            // zset
            redisUtil.zadd(cacheKey, JsonUtil.toJsonString(cacheObj), System.currentTimeMillis(),
                    RedisKeyEnum.USER_CTC_STUDY_HISTORY_ZSET_CACHE.getExpireTime());

        }
    }

    private String historyCacheKey(String userid) {
        return RedisKeyEnum.USER_CTC_STUDY_HISTORY_ZSET_CACHE.getKey(userid);
    }


    /**
     * 查询用户最新学习历史(走redis查询)
     *
     * @param userid
     * @param size。 最近几条数据
     * @return
     */
    public List<ResourceDTO> getLastHistoryFromCache(String userid, int size) {
        String cacheKey = historyCacheKey(userid);
        if (!redisUtil.exists(cacheKey)) {
            // 缓存不存在/失效。主动刷新一次
            this.initUserStudyHistoryCache(userid);
        }
        // 走缓存查询用户学习历史
        Set<String> caches = redisUtil.zreverseRange(cacheKey, 0, size);
        if (CollectionUtils.isEmpty(caches)) {
            return Collections.emptyList();
        }
        List<ResourceBaseDTO> resources = caches.stream().map(c ->
                                      JsonUtil.fromJson(c, ResourceBaseDTO.class)).collect(Collectors.toList());
        /**
         * 填充问答、视频、文章数据（走资源缓存查询）
         */
        List<Long> videoIdList = new ArrayList<>();
        List<Long> expIdList = new ArrayList<>();
        List<Long> questionIdList = new ArrayList<>();
        for (ResourceBaseDTO resource : resources) {
            if (ResourceTypeEnum.CTC_VIDEO.getType().equals(resource.getResourceType())) {
                videoIdList.add(resource.getResourceId());
            } else if (ResourceTypeEnum.CTC_EXPERIENCE.getType().equals(resource.getResourceType())) {
                expIdList.add(resource.getResourceId());
            } else if (ResourceTypeEnum.CTC_QUESTION.getType().equals(resource.getResourceType())) {
                questionIdList.add(resource.getResourceId());
            }
        }
        /**
         * 走资源缓存查询各类型资源详情
         *
         */
        List<CTCExperienceBaseVO> exps = new ArrayList<>();
        List<CTCVideoBaseVO> videos = new ArrayList<>();
        List<CTCQuestionBaseVO> questions = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(expIdList)) {
            exps = experienceService.batchGetExperienceBaseFromCache(expIdList);
        }
        if (CollectionUtils.isNotEmpty(videoIdList)) {
            videos = videoService.batchGetQuestionListFromCache(videoIdList);
        }
        if (CollectionUtils.isNotEmpty(questionIdList)) {
            questions = questionService.batchGetQuestionListFromCache(questionIdList);
        }
        return buildLastHistoryResources(resources, exps, videos, questions);
    }

    private List<ResourceDTO> buildLastHistoryResources(List<ResourceBaseDTO> resources,
                                                        List<CTCExperienceBaseVO> exps,
                                                        List<CTCVideoBaseVO> videos,
                                                        List<CTCQuestionBaseVO> questions) {
        Map<Long, CTCExperienceBaseVO> expMap;
        Map<Long, CTCVideoBaseVO> videoMap;
        Map<Long, CTCQuestionBaseVO> questionMap;
        if (CollectionUtils.isNotEmpty(exps)) {
            expMap = exps.stream().collect(Collectors.toMap(CTCExperienceBaseVO::getId, c -> c));
        } else {
            expMap = Collections.emptyMap();
        }
        if (CollectionUtils.isNotEmpty(videos)) {
            videoMap = videos.stream().collect(Collectors.toMap(CTCVideoBaseVO::getId, c -> c));
        } else {
            videoMap = Collections.emptyMap();
        }
        if (CollectionUtils.isNotEmpty(questions)) {
            questionMap = questions.stream().collect(Collectors.toMap(CTCQuestionBaseVO::getId, c -> c));
        } else {
            questionMap = Collections.emptyMap();
        }

        return resources.stream().map(h -> {

            String title = "", description = "", cover = "";
            Long time = null;
            if (ResourceTypeEnum.CTC_VIDEO.getType().equals(h.getResourceType())) {
                CTCVideoBaseVO video = videoMap.get(h.getResourceId());
                title = video.getTitle();
                description = video.getDescription();
                cover = video.getCover();
                time = video.getCreateTime();
            } else if (ResourceTypeEnum.CTC_EXPERIENCE.getType().equals(h.getResourceType())) {
                CTCExperienceBaseVO exp = expMap.get(h.getResourceId());
                title = exp.getTitle();
                description = exp.getDescription();
                cover = exp.getCover();
                time = exp.getCreateTime();
            } else if (ResourceTypeEnum.CTC_QUESTION.getType().equals(h.getResourceType())) {
                CTCQuestionBaseVO question = questionMap.get(h.getResourceId());
                title = question.getTitle();
                description = question.getDescription();
                time = question.getCreateTime();
            }
            return new ResourceDTO(
                    h.getResourceId(), h.getResourceType(),
                    ResourceTypeEnum.getByResourceTypeName(h.getResourceType()),
                    title, description, cover, time);
        }).collect(Collectors.toList());
    }

    private void initUserStudyHistoryCache(String userid) {
        List<UserStudyHistoryEntity> entities = userStudyHistoryDAO.findAllByUserId(userid);
        if (CollectionUtils.isEmpty(entities)) {
            return;
        }
        /**
         * 批量初始化用户学习历史zset缓存
         */
        String cacheKey = historyCacheKey(userid);
        Set<ZSetOperations.TypedTuple<String>> tuples = entities.stream().map(res -> {
            String value = JsonUtil.toJsonString(new ResourceBaseDTO(res.getResourceType(), res.getResourceId()));
            Double score = Double.valueOf(res.getLastDate().getTime());
            return new DefaultTypedTuple<>(value, score);
        }).collect(Collectors.toSet());
        redisUtil.zadd(cacheKey, tuples, RedisKeyEnum.USER_CTC_STUDY_HISTORY_ZSET_CACHE.getExpireTime());
    }
}
